package pl.th.permission.plugin;

import com.android.tools.r8.utils.F;

import org.objectweb.asm.Opcodes;
import org.objectweb.asm.Type;

import java.io.File;
import java.util.List;
import java.util.Set;
import java.util.StringJoiner;

import pl.th.permission.plugin.node.annotation.AnnType;

public class Utils {

    private final static String CONSTRUCTOR_NAME = "<init>";
    private final static String CONSTRUCTOR_DESC = "()V";
    private final static String PERMISSION_CALLBACK_METHOD_NAME = "onRequestPermissionsResult";
    private final static String PERMISSION_CALLBACK_METHOD_DESCRIPTOR = "(I[Ljava/lang/String;[I)V";
    public final static String ANNOTATION_NEED_PERMISSION_DESCRIPTOR = "Lpl/th/permission/annotation/NeedPermission;";
    private final static String ANNOTATION_GRANT_DESCRIPTOR = "Lpl/th/permission/annotation/OnPermissionGranted;";
    private final static String ANNOTATION_ALL_GRANT_DESCRIPTOR = "Lpl/th/permission/annotation/OnAllPermissionsGranted;";
    private final static String ANNOTATION_ANY_GRANT_DESCRIPTOR = "Lpl/th/permission/annotation/OnAnyPermissionsGranted;";
    private final static String ANNOTATION_GRANT_CALLBACK_DESCRIPTOR = "Lpl/th/permission/annotation/OnGrantedCallback;";
    private final static String ANNOTATION_DENIED_DESCRIPTOR = "Lpl/th/permission/annotation/OnPermissionDenied;";
    private final static String ANNOTATION_ANY_DENIED_DESCRIPTOR = "Lpl/th/permission/annotation/OnAnyPermissionsDenied;";
    private final static String ANNOTATION_ALL_DENIED_DESCRIPTOR = "Lpl/th/permission/annotation/OnAllPermissionsDenied;";
    private final static String ANNOTATION_DENIED_CALLBACK_DESCRIPTOR = "Lpl/th/permission/annotation/OnDeniedCallback;";


    public final static String CALLBACK_INTERFACE_NAME = "pl/th/permission/Callback";
    public final static String REQUEST_CODE_NAME = "requestCode";
    public final static String PERMISSIONS_NAME = "permissions";
    public final static String PROXY_CLZ_NAME = "fragmentClz";
    public final static String NEED_INJECT_A_METHOD_OWNER = "pl/th/permission/PermissionCompat";
    public final static String NEED_INJECT_A_METHOD_NAME = "requestPermission";
    public final static String NEED_INJECT_A_CALLBACK_METHOD_NAME = "requestOrCallbackPermission";
    public final static String NEED_INJECT_A_METHOD_DESC = "(I[Ljava/lang/String;Ljava/lang/Class;Ljava/lang/Object;)Z";
    public final static String NEED_INJECT_A_CALLBACK_METHOD_DESC = "(I[Ljava/lang/String;Ljava/lang/Class;Ljava/lang/Object;)V";
    public final static String METHOD_DESC_CALLBACK = "(I[Ljava/lang/String;)V";
    public final static String METHOD_DESC_ANY_CALLBACK = "([Ljava/lang/String;)V";
    public final static String METHOD_DESC_PARTIAL_OR_ALL_CALLBACK = "()V";


    public final static String GRANT_METHOD_NAME = "onPermissionGranted";
    public final static String DENIED_METHOD_NAME = "onPermissionDenied";

    final static String STR_ARR_EQUAL_METHOD_NAME = "contentEquals";
    final static String STR_ARR_EQUAL_METHOD_OWNER = "pl/th/permission/Utils";
    final static String STR_ARR_EQUAL_METHOD_DESC = "([Ljava/lang/String;[Ljava/lang/String;)Z";

    public static final Type PROXY_TYPE = Type.getType("Lpl/th/permission/DefaultProxyFragment;");

    public static boolean isAnnotatedByGrantedCallback(String desc) {
        return ANNOTATION_GRANT_CALLBACK_DESCRIPTOR.equals(desc);
    }

    public static boolean isAnnotatedByDeniedCallback(String desc) {
        return ANNOTATION_DENIED_CALLBACK_DESCRIPTOR.equals(desc);
    }

    public static boolean isCallbackMethod(String name, String desc) {
        return METHOD_DESC_CALLBACK.equals(desc) && (GRANT_METHOD_NAME.equals(name) || DENIED_METHOD_NAME.equals(name));
    }

    public static boolean isStaticMethod(int access) {
        return (access & Opcodes.ACC_STATIC) != 0;
    }

    public static boolean isConstructor(String name, String desc) {
        return CONSTRUCTOR_NAME.equals(name) && CONSTRUCTOR_DESC.equals(desc);
    }

    public static boolean isPermissionCallbackMethod(String name, String desc) {
        return PERMISSION_CALLBACK_METHOD_NAME.equals(name)
                && PERMISSION_CALLBACK_METHOD_DESCRIPTOR.equals(desc);
    }

    public static boolean isAnnotateMethodByNA(String desc) {
        return ANNOTATION_NEED_PERMISSION_DESCRIPTOR.equals(desc);
    }

    public static boolean isAnnotateMethodByG(String desc) {
        return ANNOTATION_GRANT_DESCRIPTOR.equals(desc);
    }

    public static boolean isAnnotatedByAllGranted(String desc) {
        return ANNOTATION_ALL_GRANT_DESCRIPTOR.equals(desc);
    }

    public static boolean isAnnotatedByAllDenied(String desc) {
        return ANNOTATION_ALL_DENIED_DESCRIPTOR.equals(desc);
    }

    public static boolean isAnnotateMethodByD(String desc) {
        return ANNOTATION_DENIED_DESCRIPTOR.equals(desc);
    }

    public static boolean isAnnotatedByAnyDenied(String desc) {
        return ANNOTATION_ANY_DENIED_DESCRIPTOR.equals(desc);
    }

    public static boolean isAnnotatedByAnyGranted(String desc) {
        return ANNOTATION_ANY_GRANT_DESCRIPTOR.equals(desc);
    }

    public static String getCallbackInterfaceName() {
        return CALLBACK_INTERFACE_NAME;
    }

    public static String getRequestCodeName() {
        return REQUEST_CODE_NAME;
    }

    public static String getPermissionsName() {
        return PERMISSIONS_NAME;
    }

    public static String getFragmentClzName() {
        return PROXY_CLZ_NAME;
    }

    public static String getAnnDesByType(AnnType type) {
        switch (type) {
            case NEED_PERMISSION:
                return Utils.ANNOTATION_NEED_PERMISSION_DESCRIPTOR;
            case ON_GRANTED_CALLBACK:
                return Utils.ANNOTATION_GRANT_CALLBACK_DESCRIPTOR;
            case ON_DENIED_CALLBACK:
                return Utils.ANNOTATION_DENIED_CALLBACK_DESCRIPTOR;
            case ON_PERMISSION_GRANTED:
                return Utils.ANNOTATION_GRANT_DESCRIPTOR;
            case ON_PERMISSION_DENIED:
                return Utils.ANNOTATION_DENIED_DESCRIPTOR;
            case ON_ANY_PERMISSION_GRANTED:
                return Utils.ANNOTATION_ANY_GRANT_DESCRIPTOR;
            case ON_ANY_PERMISSION_DENIED:
                return Utils.ANNOTATION_ANY_DENIED_DESCRIPTOR;
            case ON_ALL_PERMISSION_GRANTED:
                return ANNOTATION_ALL_GRANT_DESCRIPTOR;
            case ON_ALL_PERMISSION_DENIED:
                return ANNOTATION_ALL_DENIED_DESCRIPTOR;
            default:
                throw new RuntimeException("unsupported annotation type: " + type);
        }
    }

    public static String toString(List<?> list) {
        StringBuilder builder = new StringBuilder();
        builder.append("{");
        int size = list.size();;
        for (int i = 0; i < size; i++) {
            builder.append(list.get(i).toString());
            if (i != size - 1) {
                builder.append(",");
            }
        }
        builder.append("}");
        return builder.toString();
    }

    public static boolean isListEmpty(List<?> list) {
        return list == null || list.size() > 0;
    }

    static boolean isEqualSet(Set<String> s1, Set<String> s2) {
        if (s1.size() != s2.size()) return false;
        return s1.containsAll(s2);
    }

    static String toString(Set<?> set) {
        StringJoiner joiner = new StringJoiner("{", ",", "}");
        for (Object o : set) {
            joiner.add(o.toString());
        }
        return joiner.toString();
    }

    @SuppressWarnings("ResultOfMethodCallIgnored")
    public static void mkdirs(String path) {
        if (path == null) return;
        File file = new File(path);
        synchronized (Utils.class) {
            if (!file.exists()) {
                file.mkdirs();
            }
        }
    }
}
